/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#include "gimisubscribers.h"

namespace gimi {

KeepAlive::KeepAlive()
  : active (false),
    interval (0),
    nextrun (0)
{}
  
KeepAlive::KeepAlive(const int intervalMs)
  : active (true),
    interval (intervalMs),
    nextrun (0)
{}
    
KeepAlive::~KeepAlive()
{}

bool KeepAlive::isNextRunTime() const
{
  if (!active)
    return false;
  
  gim::time currentTime(true);
  if (nextrun < currentTime) {
    return true;
  } else {
    return false;
  }
}
    
bool KeepAlive::isNextRunTimeUpdate()
{
  if (!active)
    return false;
  
  gim::time currentTime(true);
  if (nextrun < currentTime) {
    nextrun = currentTime;
    nextrun += interval;
    return true;
  } else {
    return false;
  }
}
    
void KeepAlive::updateNextRunTime()
{
  if (!active)
    return;
  
  gim::time currentTime(true);
  
  nextrun = currentTime;
  nextrun += interval;
}
  
GIMIUnrepliedMessage::GIMIUnrepliedMessage()
  : messageUid (0),
    sentTime ()
{

}

GIMIUnrepliedMessage::GIMIUnrepliedMessage(const uint32_t mUid,
                                           const gim::time &sTime)
  : messageUid (mUid),
    sentTime (sTime)
{
  
}

GIMIUnrepliedMessage::~GIMIUnrepliedMessage()
{

}

bool GIMIUnrepliedMessage::operator== (const GIMIUnrepliedMessage& right) const
{
  if (sentTime == right.sentTime && messageUid == right.messageUid)
    return true;
  return false;
}

bool GIMIUnrepliedMessage::operator< (const GIMIUnrepliedMessage& right) const
{
  return (sentTime < right.sentTime);
}

bool GIMIUnrepliedMessage::operator> (const GIMIUnrepliedMessage& right) const
{
  return (sentTime > right.sentTime);
}
  
GIMISubscriber::GIMISubscriber()
  : clientId (0),
    unreplied ()
{

}

GIMISubscriber::GIMISubscriber(const int cId)
  : clientId (cId),
    unreplied ()
{

}
   
GIMISubscriber::~GIMISubscriber()
{

}
    
int GIMISubscriber::getId() const
{
  return clientId;
}
    
int GIMISubscriber::getUnrepliedCount() const
{
  return unreplied.size();
}

int GIMISubscriber::sendTo(const int maxUnsynced,
                           const gim::time &timeouted,
                           const uint32_t msgUid,
                           const gim::time &sendTime)
{
  dPrint(20, "Called with maxUnsynced: %d, msgUid: %d", maxUnsynced, msgUid);
  bool removeFromQueue = false;
  int unsynced = 0, n = 0;
  t_unrepliedIterator iter;

  // Check timeouted first, and remove them.
  for (iter = unreplied.begin();
       iter != unreplied.end();
       ++iter)
  {
    /*
    dPrint(25, "Senttime for timeouted %d %d.%.6d timeouted %d.%.6d",
           n, (*iter).sentTime.seconds, (*iter).sentTime.useconds,
           timeouted.seconds, timeouted.useconds);
    */
    if((*iter).sentTime < timeouted) {
      removeFromQueue = true;
      ++n;
    } else {
      break;
    }
  }
  
  dPrint(30, "Clientid: %d, found %d timeouted messages in unreplied queue.",
         clientId, n);
  dPrint(30, "Unreplied queue length is %d", unreplied.size());
  
  // Remove range from list.
  if (removeFromQueue) {
    --iter;
    if (iter == unreplied.begin()) {
      unreplied.erase(unreplied.begin());
    } else {
      unreplied.erase(unreplied.begin(), iter);
    }
    dPrint(15, "Removed %d timeouted messages from unreplied list.\n", n);
  }

  unsynced = getUnrepliedCount();

  dPrint(20, "%d unsynced messages, maxUnsync is %d.", unsynced, maxUnsynced);
  if (unsynced > maxUnsynced) { // Too many unsynced messages.
    return 0;
  }

  // Add current info to unsynced list.
  if (!(unreplied.insert(GIMIUnrepliedMessage(msgUid, sendTime))).second) {
    dPrint(3, "Could not add new unreplied message muid:%ud to client 0x%08x. "
           "Internal error.", msgUid, getId());
    return 0;
  }

  dPrint(20, "Added a new unreplied for 0x%08x with msguid %u.", getId(), msgUid);
  return getId();
}

int GIMISubscriber::handleReply(const uint32_t msgUid)
{
  t_unrepliedIterator iter;

  // Check timeouted first, and remove them.
  for (iter = unreplied.begin();
       iter != unreplied.end();
       ++iter)
  {
    if((*iter).messageUid == msgUid) {
      unreplied.erase(iter);
      dPrint(20, "Found msgUid %u and removed from unreplied.", msgUid);
      return GIMI_OK;
    }
  }

  return GIMI_EMPTY;
}
  
GIMISubscriberContainer::GIMISubscriberContainer(const std::string sDescr,
                                                 const std::string sLoc)
  : subscribers (),
    serviceDescription (sDescr),
    serviceLocation (sLoc),
    serviceKeepAlive ()
{}
  
GIMISubscriberContainer::GIMISubscriberContainer(const std::string sDescr,
                                                 const std::string sLoc,
                                                 const int keepAliveInterval)
  : subscribers (),
    serviceDescription (sDescr),
    serviceLocation (sLoc),
    serviceKeepAlive (keepAliveInterval)
{}

GIMISubscriberContainer::~GIMISubscriberContainer()
{
  // Loop through and delete subscribers.
  for (t_subscribersIterator iter = subscribers.begin();
       iter != subscribers.end();
       ++iter)
  {
    if ((*iter).second != NULL) {
      delete (*iter).second;
    }
  }
}

GIMISubscriberContainer::GIMISubscriberContainer(const GIMISubscriberContainer& container)
  : subscribers (container.subscribers),
    serviceDescription (container.serviceDescription),
    serviceLocation (container.serviceLocation),
    serviceKeepAlive (container.serviceKeepAlive)
{
  dPrint(25, "GIMISubscriberContainer assigment.");
}

const GIMISubscriberContainer& GIMISubscriberContainer::operator=
    (const GIMISubscriberContainer& right)
{
  dPrint(25, "GIMISubscriberContainer =");
  subscribers = right.subscribers;
  serviceDescription = right.serviceDescription;
  serviceLocation = right.serviceLocation;
  serviceKeepAlive = right.serviceKeepAlive;

  return *this;
}

bool GIMISubscriberContainer::insert(const int clientId)
{
  GIMISubscriber *newclient = new GIMISubscriber(clientId);
  
  // Add to set.
  bool result = (subscribers.insert(t_subscriberPair(clientId, newclient))).second;
  
  return result;
}

int GIMISubscriberContainer::erase(const int clientId)
{
  // Remove from set.
  t_subscribersIterator citer = subscribers.find(clientId);

  if (citer == subscribers.end()) { // Given clientId not found.
    return 0;
  }

  // Delete the pointer.
  delete (*citer).second;

  // Erase the entry from map.
  subscribers.erase(citer);
  
  return 1;
}

int GIMISubscriberContainer::size() const
{
  return subscribers.size();
}

bool GIMISubscriberContainer::empty() const
{
  return subscribers.empty();
}

std::string GIMISubscriberContainer::getDescription() const
{
  return serviceDescription;
}

std::string GIMISubscriberContainer::getLocation() const
{
  return serviceLocation;
}

int GIMISubscriberContainer::getArray(int **subArray,
                                      const int maxSync,
                                      const gim::time &currentTime,
                                      const gim::time &timeout,
                                      const uint32_t msgUid)
{
  dPrint(10, "Called with maxSync: %d, msgUid: %d", maxSync, msgUid);
  if (subArray == NULL) {
    return -1;
  }

  int clientId = 0, n = 0;
  gim::time timeoutTime = currentTime - timeout;

  *subArray = new int32_t[size()];
  
  t_subscribersIterator siter;
  if (maxSync == -1) { // Don't use maxSync
    for (siter = subscribers.begin();
         siter != subscribers.end();
         ++siter)
    {
      (*subArray)[n] = ((*siter).second)->getId();
      ++n;
    }

  } else { // Use maxSync
    for (siter = subscribers.begin();
         siter != subscribers.end();
         ++siter)
    {
      clientId = ((*siter).second)->sendTo(maxSync, timeoutTime, msgUid, currentTime);
      if (clientId != 0) {
        (*subArray)[n] = clientId;
        ++n;
      }
    }
  }
  
  return n;
}

int GIMISubscriberContainer::getSubscriberList(t_clientIdList &subscriberList,
                                               const int maxSync,
                                               const gim::time &currentTime,
                                               const gim::time &timeout,
                                               const uint32_t msgUid)
{
  dPrint(10, "Called with maxSync: %d, msgUid: %d", maxSync, msgUid);

  int clientId = 0, n = 0;
  gim::time timeoutTime = currentTime - timeout;
  
  t_subscribersIterator siter;
  if (maxSync == -1) { // Don't use maxSync
    for (siter = subscribers.begin();
         siter != subscribers.end();
         ++siter)
    {
      subscriberList.push_back(((*siter).second)->getId());
      ++n;
    }

  } else { // Use maxSync
    for (siter = subscribers.begin();
         siter != subscribers.end();
         ++siter)
    {
      clientId = ((*siter).second)->sendTo(maxSync, timeoutTime, msgUid, currentTime);
      if (clientId != 0) {
        subscriberList.push_back(((*siter).second)->getId());
        ++n;
      }
    }
  }
  
  return n;
}

int GIMISubscriberContainer::replyToClient(const uint32_t msgUid,
                                           const int clientId)
{
  dPrint(18, "Called");
  int result = GIMI_ERROR;
  t_subscribersIterator citer = subscribers.find(clientId);
  if (citer == subscribers.end()) {
    dPrint(5, "Client %d not found", clientId);
    return GIMI_INVALIDCLIENT;
  }

  result = (citer->second)->handleReply(msgUid);

  return result;
}

/*
GIMIServiceInfo::GIMIServiceInfo(int majorSid,
                                 int minorSid,
                                 std::string sDescr,
                                 std::string sLoc)
  : majorServiceId (majorSid),
    minorServiceId (minorSid),
    serviceDescription (sDescr),
    serviceLocation (sLoc)
{
}
*/

GIMISubscribers::GIMISubscribers()
  : subscriberMutex (ownMutex_Init()),
    services ()
{
  assert(subscriberMutex != NULL);
}

GIMISubscribers::~GIMISubscribers()
{
  // Loop through all types and delete gimisubscribers.
  for (t_servicesIterator siter = services.begin();
       siter != services.end();
       ++siter)
  {
    for (t_servicesMinorIterator miter = (siter->second).begin();
         miter != (siter->second).end();
         ++miter)
    {
      if (miter->second != NULL) {
        delete miter->second;
        miter->second = NULL;
      }
    }
  }
  
  ownMutex_Destroy(subscriberMutex);
}

bool GIMISubscribers::isRegisteredType(const int majorTypeId,
                                       const int minorTypeId)
{  
  bool result = false;
  
  ownMutex_Lock(subscriberMutex);
  
  // Search service by Major ID.
  t_servicesIterator ma_iter = services.find(majorTypeId);
  if (ma_iter != services.end()) { 
    // Majortype found. Now try to search the Minortype.
    t_servicesMinorIterator mi_iter = (ma_iter->second).find(minorTypeId);
    if (mi_iter != (ma_iter->second).end())
      result = true;
  }
  
  ownMutex_Unlock(subscriberMutex);
  
  return result;
}

bool GIMISubscribers::addServiceType(const int majorTypeId,
                                     const int minorTypeId,
                                     const std::string serviceDescription,
                                     const std::string serviceLocation,
                                     const int keepAliveInterval /* = 0 */)
{
  dPrint(12, "Called with %d %d %s %s %d", majorTypeId, minorTypeId,
      serviceDescription.c_str(), serviceLocation.c_str(), keepAliveInterval);
  bool result = false;
  
  ownMutex_Lock(subscriberMutex);
  
  t_servicesIterator siter = services.find(majorTypeId);
  t_servicesMinorIterator miter;
  GIMISubscriberContainer *gs = NULL;

  if (siter == services.end()) { // Major-type not found, add it.
    // Add and assign iterator to the new type.
    siter = (services.insert(t_servicesMinorPair(majorTypeId,
             t_servicesMinor()))).first;
  }

  miter = (siter->second).find(minorTypeId);

  if (miter == (siter->second).end()) { // Minor-type not found, add it.
    if (keepAliveInterval < 1) {
      gs = new GIMISubscriberContainer(serviceDescription, serviceLocation);
    } else {
      gs = new GIMISubscriberContainer(serviceDescription, serviceLocation,
                                       keepAliveInterval);
    }
    (siter->second).insert(t_subscribersPair(minorTypeId, gs));

    dPrint(18, "Service type %d:%d (%s) @ %s added.", majorTypeId, minorTypeId,
           serviceDescription.c_str(), serviceLocation.c_str());
    result = true;
  } else { // Minor-type exists already.
    dPrint(4, "Service type %d:%d already exists.", majorTypeId, minorTypeId);
    result = false;
  }

  ownMutex_Unlock(subscriberMutex);

  return result;
}

bool GIMISubscribers::removeServiceType(const int majorTypeId,
                                        const int minorTypeId
                                            /* = GIMI_IGNOREMINORTYPE */)
{
  int result = 0;
  bool retVal = false;

  ownMutex_Lock(subscriberMutex);

  t_servicesIterator siter = services.find(majorTypeId);
  
  if (siter != services.end()) { // Major-type found.
    if (minorTypeId == GIMI_IGNOREMINORTYPE) { // Remove major-type.
      while (!(siter->second).empty()) // Loop through all minor-types of selected major-type.
      {
        if (((siter->second).begin())->second != NULL) { // If GIMISubscribers* is not null, delete it.
          delete ((siter->second).begin())->second;
          ((siter->second).begin())->second = NULL;
        }
        (siter->second).erase((siter->second).begin());
      }
      result = services.erase(majorTypeId);
    } else { // Remove only minor-type.
      t_servicesMinorIterator miter = (siter->second).find(minorTypeId);
      if (miter != (siter->second).end()) {
        if (miter->second != NULL) { // If GIMISubscribers* is not null, delete it.
          delete miter->second;
        }
        (siter->second).erase(miter); // Delete minor-type
        result = 1;
      }
    }

    if (result == 0) {
      dPrint(4, "Removing failed because servicetype %d:%d was not found.",
             majorTypeId, minorTypeId);
      retVal = false;
    } else if (result != 1) {
      dPrint(4, "Removing serviceType %d:%d returned %d. Internal error!",
             majorTypeId, minorTypeId, result);
      retVal = false;
      assert(false);
    } else {
      dPrint(18, "Service type %d:%d removed.", majorTypeId, minorTypeId);
      retVal = true;
    }
  } else {
    dPrint(4, "Service major-type %d not found.", majorTypeId);
    retVal = false;
  }
  ownMutex_Unlock(subscriberMutex);
  
  return retVal;
}

int GIMISubscribers::listServices(t_serviceList &serviceList)
{
  t_servicesIterator majorIter;
  t_servicesMinorIterator minorIter;
  int n = 0;

  ownMutex_Lock(subscriberMutex);
  
  for (majorIter = services.begin();
       majorIter != services.end();
       ++majorIter)
  {
    for (minorIter = (majorIter->second).begin();
         minorIter != (majorIter->second).end();
         ++minorIter)
    {
      if (majorIter->first == GIMI_PROTOCOL_CLIENT_DESCRIPTION) {
        serviceList.push_back(GIMIService(0, "", GIMI_SERVICETYPE_DESCRIPTION,
                                          majorIter->first,
                                          minorIter->first,
                                          minorIter->second->getDescription(),
                                          minorIter->second->getLocation()));
        ++n;
      } else {
        serviceList.push_back(GIMIService(0, "", GIMI_SERVICETYPE_PROVIDED,
                                          majorIter->first,
                                          minorIter->first,
                                          minorIter->second->getDescription(),
                                          minorIter->second->getLocation()));
        ++n;
      }
    }
  }

  ownMutex_Unlock(subscriberMutex);
  
  return n;
}

int GIMISubscribers::listServicesForKeepAlive(t_serviceNumberList &serviceList)
{
  t_servicesIterator majorIter;
  t_servicesMinorIterator minorIter;
  int n = 0;

  ownMutex_Lock(subscriberMutex);
  
  for (majorIter = services.begin();
       majorIter != services.end();
       ++majorIter)
  {
    for (minorIter = (majorIter->second).begin();
         minorIter != (majorIter->second).end();
         ++minorIter)
    {
      if (minorIter->second->serviceKeepAlive.isNextRunTimeUpdate() &&
          !minorIter->second->empty())
      {
        serviceList.push_back(t_serviceNumberPair(majorIter->first,
                              minorIter->first));
        ++n;
      }
    }
  }

  ownMutex_Unlock(subscriberMutex);
  
  return n;
}

int GIMISubscribers::addSubscriber(const int clientId,
                                   const int majorTypeId,
                                   const int minorTypeId)
{
  int result = GIMI_ERROR;
  
  ownMutex_Lock(subscriberMutex);
  
  t_servicesIterator siter = services.find(majorTypeId);
  t_servicesMinorIterator miter;

  if (siter != services.end()) { // Major-type found.
    miter = (siter->second).find(minorTypeId);

    if (miter != (siter->second).end()) { // Minor-type found.
      if ((miter->second)->insert(clientId)) { // Clientid inserted.
        //dPrint(ODTEST, "Subscriber %d added to %d:%d.", clientId, majorTypeId,
        //       minorTypeId);
        result = GIMI_OK;
      } else { // Clientid already exists
        //dPrint(ODTEST, "Could not add client to %d%d, given clientId %d already exists.",
        //       majorTypeId, minorTypeId, clientId);
        result = GIMI_INVALIDCLIENT;
      }
    } else { // Minor-type not found.
      dPrint(ODTEST, "Service type %d:%d not found.", majorTypeId, minorTypeId);
      result = GIMI_INVALIDTYPE;
    }
  } else { // Major-type not found.
    dPrint(ODTEST, "Service major-type %d not found.", majorTypeId);
    result = GIMI_INVALIDTYPE;
  }

  ownMutex_Unlock(subscriberMutex);
  
  return result;
}

int GIMISubscribers::removeSubscriber(const int clientId,
                                      const int majorTypeId,
                                      const int minorTypeId)
{
  int result = GIMI_ERROR;

  ownMutex_Lock(subscriberMutex);
  
  t_servicesIterator siter = services.find(majorTypeId);
  t_servicesMinorIterator miter;

  if (siter != services.end()) { // Major-type found.
    miter = (siter->second).find(minorTypeId);
    if (miter != (siter->second).end()) { // Minor-type found.
      int r = (miter->second)->erase(clientId);
      if (r == 0) {
        dPrint(6, "Removing clientId %d failed, was not found.");
        result = GIMI_INVALIDCLIENT;
      } else if (r == 1) {
        dPrint(18, "Subscriber %d removed from %d:%d.", clientId, majorTypeId,
               minorTypeId);
        result = GIMI_OK;
      } else {
        dPrint(1, "Removing clientId %d returned %d, internal error.");
        result = GIMI_ERROR;
        abort();
      }
    } else { // Minor-type not found.
      dPrint(6, "Service type %d:%d not found.", majorTypeId, minorTypeId);
      result = GIMI_INVALIDTYPE;
    }
  } else { // Major-type not found.
    dPrint(6, "Service major-type %d not found.", majorTypeId);
    result = GIMI_INVALIDTYPE;
  }

  ownMutex_Unlock(subscriberMutex);

  return result;
}

int GIMISubscribers::removeSubscriber(const int clientId)
{
  dPrint(10, "Called.");
  int removes = 0;

  ownMutex_Lock(subscriberMutex);
  
  if (!services.empty()) {
    // Loop through all major-types
    for (t_servicesIterator siter = services.begin();
         siter != services.end();
         ++siter)
    {
    // Loop through all minor-types
      for (t_servicesMinorIterator miter = (siter->second).begin();
           miter != (siter->second).end();
           ++miter)
      {
      // Remove clientId from minor-type if it exists.
        if ((miter->second)->erase(clientId)) {
          ++removes;
        }
      }
    }
    dPrint(18, "Removed client 0x%08x from %d services.", clientId, removes);
  } else {
    dPrint(18, "No services.");
  }

  ownMutex_Unlock(subscriberMutex);
  
  return removes;
}

int GIMISubscribers::getSubscriberCount(const int majorTypeId,
                                        const int minorTypeId
                                            /*  = GIMIBUFFER_IGNOREMINORTYPE */) const
{
  int result = GIMI_ERROR;
  ownMutex_Lock(subscriberMutex);
  
  t_servicesConstIterator siter = services.find(majorTypeId);
  t_servicesMinorConstIterator miter;

  if (siter != services.end()) {
    // Minor-type is ignored, loop through all of them.
    if (minorTypeId == GIMI_IGNOREMINORTYPE) {
      int size = 0;
      for (miter = (siter->second).begin();
           miter != (siter->second).end();
           ++miter)
      {
        size += (miter->second)->size();
      }
      result = size;
    } else { // Use given minor-type.
      miter = (siter->second).find(minorTypeId);
      if (miter == (siter->second).end()) { // Minor-type not found.
        dPrint(4, "Service type %d:%d not found.", majorTypeId, minorTypeId);
        result = GIMI_INVALIDTYPE;
      } else if ((miter->second)->empty()) {
        result = 0;
      } else {
        result = (miter->second)->size();
      }
    }

  } else { // Major-type not found.
    dPrint(4, "Service major-type %d not found.", majorTypeId);
    result = GIMI_INVALIDTYPE;
  }
  
  ownMutex_Unlock(subscriberMutex);

  return result;
}

int GIMISubscribers::getSubscribers(int **subscriberArray,
                                    int *arrayLength,
                                    const int majorTypeId,
                                    const int minorTypeId,
                                    const uint32_t msgUid,
                                    const int maxUnsynced /* = -1 */,
                                    const unsigned int timeout /* = 500 */)
{
  int result = GIMI_ERROR;
  ownMutex_Lock(subscriberMutex);
  
  if (subscriberArray != NULL && arrayLength != NULL) {
    t_servicesIterator siter = services.find(majorTypeId);
    t_servicesMinorIterator miter;

    if (siter != services.end()) {
      miter = (siter->second).find(minorTypeId);
      if (miter != (siter->second).end()) {
        // Modify timeout to GIMITime format.
        gim::time gTimeout(timeout), currentTime(true);
        dPrint(15, "Timeout: %d.%.6d", gTimeout.seconds, gTimeout.useconds);
  
        // Get subscribers by maxUnsynced
        *arrayLength = (miter->second)->getArray(subscriberArray, maxUnsynced,
        currentTime, gTimeout, msgUid);
  
        result = GIMI_OK;
      } else { // Minor-type not found.
        dPrint(4, "Service type %d:%d not found.", majorTypeId, minorTypeId);
        result = GIMI_INVALIDTYPE;
      }
    } else { // Major-type not found.
      dPrint(4, "Service major-type %d not found.", majorTypeId);
      result = GIMI_INVALIDTYPE;
    }
  } else {
    dPrint(4, "Given addresses to output should not be null!");
    result = GIMI_ERROR;
  }

  ownMutex_Unlock(subscriberMutex);

  return result;
}

int GIMISubscribers::getSubscribers(t_clientIdList &subscriberList,
                                    const int majorTypeId,
                                    const int minorTypeId,
                                    const uint32_t msgUid,
                                    const int maxUnsynced /* = -1 */,
                                    const unsigned int timeout /* = 500 */)
{
  int result = GIMI_ERROR;
  ownMutex_Lock(subscriberMutex);
  
  t_servicesIterator siter = services.find(majorTypeId);
  t_servicesMinorIterator miter;

  if (siter != services.end()) {
    miter = (siter->second).find(minorTypeId);
    if (miter != (siter->second).end()) {
      // Modify timeout to GIMITime format.
      gim::time gTimeout(timeout), currentTime(true);
      dPrint(15, "Timeout: %d.%.6d", gTimeout.seconds, gTimeout.useconds);

      // Get subscribers by maxUnsynced
      (miter->second)->getSubscriberList(subscriberList, maxUnsynced, currentTime,
          gTimeout, msgUid);

      result = GIMI_OK;
    } else { // Minor-type not found.
      dPrint(4, "Service type %d:%d not found.", majorTypeId, minorTypeId);
      result = GIMI_INVALIDTYPE;
    }
  } else { // Major-type not found.
    dPrint(4, "Service major-type %d not found.", majorTypeId);
    result = GIMI_INVALIDTYPE;
  }

  ownMutex_Unlock(subscriberMutex);

  return result;
}

int GIMISubscribers::replyToMessage(const uint32_t msgUid,
                                    const int majorTypeId,
                                    const int minorTypeId,
                                    const int clientId)
{
  dPrint(16, "Called");
  int result = GIMI_ERROR;

  ownMutex_Lock(subscriberMutex);
  
  t_servicesIterator siter = services.find(majorTypeId);
  t_servicesMinorIterator miter;

  if (siter == services.end()) { // Major-type not found.
    dPrint(4, "Service major-type %d not found.", majorTypeId);
    result = GIMI_INVALIDTYPE;
  } else {
    miter = (siter->second).find(minorTypeId);
    if (miter == (siter->second).end()) { // Minor-type not found.
      dPrint(4, "Service type %d:%d not found.", majorTypeId, minorTypeId);
      result = GIMI_INVALIDTYPE;
    } else {
      result = (miter->second)->replyToClient(msgUid, clientId);
    }
  }

  ownMutex_Unlock(subscriberMutex);

  return result;
}

} // end namespace gimi
